package com.kiss.fw.context;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.mail.Address;
import javax.mail.internet.InternetAddress;

import com.kiss.fw.exceptions.ValidationException;
import com.kiss.fw.utils.Constants;
import com.kiss.fw.utils.Util;

public class EmailContext extends AbstractContext implements Serializable {
	private static Map<Class<?>, Map<String, EmailContext>> instances = new HashMap<Class<?>, Map<String, EmailContext>>();
	
	private String jndi;
	private String subject;
	private String body;
	private Address from;
	private List<Address> to;
	private List<String> attachments;
	
	public EmailContext(final String _subject, final String _body, final String _from, final List<String> _to, final List<String> _attachments) throws ValidationException {
		if(_to == null || _to.size() == 0) {
			throw Constants.Exceptions.VALIDATION_EXCEPTION;
		}
		
		subject = Util.trimString(_subject);
		body = Util.trimString(_body);
		attachments = _attachments;
		jndi = Util.getProperty(Constants.Property.EMAIL_JNDI);
		to = new ArrayList<Address>(_to.size());
		
		if(Util.isValidEmail(_from)) {
			try {
				from = new InternetAddress(_from);
			}
			catch(Exception _exception) {
			}
		}
		
		for(String tempTo : _to) {
			if(Util.isValidEmail(tempTo)) {
				try {
					to.add(new InternetAddress(tempTo));
				}
				catch(Exception _exception) {
				}
			}
		}
	}
	
	private EmailContext(final String _subject, final String _body, final String _from, List<String> _to, final List<String> _attachments, final String _jndi) throws ValidationException {
		subject = Util.trimString(_subject);
		body = Util.trimString(_body);
		attachments = _attachments;
		
		String fromEmail = Util.isStringEmpty(_from) ? Util.getProperty(Constants.Property.NOTIFICATIONS_EMAIL_FROM) : _from;
		
		if(Util.isValidEmail(fromEmail)) {
			try {
				from = new InternetAddress(fromEmail);
			}
			catch(Exception _exception) {
			}
		}
		
		String toEmail = _to == null || _to.size() == 0 ? Util.getProperty(Constants.Property.NOTIFICATIONS_EMAIL_TO) : null;
		
		if(!Util.isStringEmpty(toEmail)) {
			_to = Arrays.asList(Util.toStringArray(toEmail, ","));
		}
		
		to = new ArrayList<Address>(_to.size());
		
		for(String tempTo : _to) {
			if(Util.isValidEmail(tempTo)) {
				try {
					to.add(new InternetAddress(tempTo));
				}
				catch(Exception _exception) {
				}
			}
		}
		
		if(Util.isStringEmpty(_jndi)) {
			jndi = Util.getProperty(Constants.Property.NOTIFICATIONS_EMAIL_JNDI);
		}
		else {
			jndi = _jndi;
		}
		
		if(to.size() == 0) {
			throw Constants.Exceptions.VALIDATION_EXCEPTION;
		}
	}
	
	/**
	 * Gets an instance for the class specified.  This should be used for notifications that
	 * are configured based on the specified class and or method.
	 * 
	 * @param Class<?> _class
	 * @param String _method
	 * @return EmailContext
	 * @throws ValidationException
	 */
	public static synchronized EmailContext getNotificationEmailContext(final Class<?> _class, final String _method) throws ValidationException {
		String methodName = Util.isStringEmpty(_method) ? "*" : _method;
		
		// determine if we need to update the instances map
		if((!instances.containsKey(_class)) || (instances.containsKey(_class) && !instances.get(_class).containsKey(_method))) {
			// getting handle to the method map
			Map<String, EmailContext> methodMap = instances.get(_class);
			
			// create if required
			if(methodMap == null) {
				methodMap = new HashMap<String, EmailContext>();
				instances.put(_class, methodMap);
			}
			
			// adding the EmailContext to the specified method
			methodMap.put(methodName, getNotificationEmailContext(_class, _method, null, null));
		}
		
		return instances.get(_class).get(methodName);
	}
	
	/**
	 * Gets an instance for the class specified.  This should be used for notifications that
	 * are configured based on the specified class and or method and want to override the
	 * subject and body.
	 * 
	 * @param Class<?> _class
	 * @param String _method
	 * @param String _subject
	 * @param String _body
	 * @return EmailContext
	 * @throws ValidationException
	 */
	public static EmailContext getNotificationEmailContext(final Class<?> _class, final String _method, final String _subject, final String _body) throws ValidationException {
		StringBuffer sb = new StringBuffer();
		sb.append(Constants.Property.NOTIFICATIONS_EMAIL);
		sb.append(Util.getClassNameWithoutPackage(_class));
		sb.append('.');
		
		if(!Util.isStringEmpty(_method)) {
			sb.append(_method);
			sb.append('.');
		}
		
		String tempFrom = Util.getProperty(sb.toString() +"from");
		String tempTo = Util.getProperty(sb.toString() +"to");
		String tempSubject = Util.isStringEmpty(_subject) ? Util.getProperty(sb.toString() +"subject") : _subject;
		String tempBody = Util.isStringEmpty(_body) ? Util.getProperty(sb.toString() +"body") : _body;
		String jndi = Util.getProperty(sb.toString() +"jndi");
		
		String[] tempToArray = Util.toStringArray(tempTo, ",");
		List<String> tempToList = null;
		
		if(tempToArray != null) {
			tempToList = Arrays.asList(tempToArray);
		}
		
		return new EmailContext(tempSubject, tempBody, tempFrom, tempToList, null, jndi);
	}
	
	public String getJndi() {
		return jndi;
	}
	
	public String getSubject() {
		return subject;
	}
	
	public String getBody() {
		return body;
	}
	
	public Address getFrom() {
		return from;
	}
	
	public List<Address> getTo() {
		return to;
	}
	
	public List<String> getAttachments() {
		return attachments;
	}
}